﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Linq;
using System.Web.Mvc;
using DNA.Mvc.jQuery;
using DNA.Mvc.Controllers;
using DNA.Mvc.Notification;
using System.IO;
using DNA.Mvc.UI;
using DNA.Mvc.Community.Events;

namespace DNA.Mvc.Community.Controllers
{
    //[Log]
    //[HandleError]
    public class PostController : Controller
    {
        private IForumContext forumContext;
        //private INotificationService notify;
        private WebSiteContext webContext;

        public PostController(IForumContext dbContext, WebSiteContext websiteContext)
        {
            forumContext = dbContext;
            //notify = notifyService;
            webContext = websiteContext;
        }

        private readonly string AREA = "Community";

        [SecurityAction("Community", "Create new thread", "Allows user can create a new thread",
             TitleResName = "SA_CreateNewThread",
             DescResName = "SA_CreateNewThreadDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult New(int forumID)
        {
            ViewBag.Forum = forumContext.Forums.Find(forumID);
            return View();
        }

        [SecurityAction("Community", "Create new thread", "Allows user can create a new thread",
             TitleResName = "SA_CreateNewThread",
             DescResName = "SA_CreateNewThreadDesc",
             PermssionSetResName = "SA_Community"
         )]
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult New(int forumID, Post post, bool? pinPost, bool? lockPost, bool notifyMe)
        {
            var forum = new ForumWrapper(forumContext.Forums.Find(forumID), forumContext);

            var msg = "";
            var code = CheckUploadedFiles(out msg);

            if (code != 200)
            {
                ModelState.AddModelError("", msg);
                ViewBag.Forum = forum;
                return View(post);
            }

            if (ModelState.IsValid)
            {
                if (forum.IsModerated && forum.IsModerator(this.HttpContext))
                {
                    post.IsApproved = true;
                    post.Approved = DateTime.Now;
                    post.Moderator = User.Identity.Name;
                }
                else
                {
                    post.IsApproved = !forum.IsModerated;
                }

                post.UserName = User.Identity.Name;
                var thread = forumContext.CreateThread(forumID, post, pinPost.HasValue ? pinPost.Value : false, lockPost.HasValue ? lockPost.Value : false);
                AttachToPost(post);
                forumContext.SaveChanges();

                if (notifyMe)
                    Notifier.Subscribe(thread.SubscriptionToken, User.Identity.Name);

                new NewPostEvent(post).Raise(HttpContext);
                UpdateUserForumProfile();
                return RedirectToAction("Index", "Thread", new { Area = AREA, id = thread.ID });
            }

            ViewBag.Forum = forum;
            return View(post);
        }

        private int CheckUploadedFiles(out string message)
        {
            message = "";
            foreach (string key in Request.Files.Keys)
            {
                var file = Request.Files[key];

                if ((file.ContentLength == 0) || (string.IsNullOrEmpty(file.FileName))) continue;

                var fileInfo = new FileInfo(file.FileName);

                if (!webContext.RootWeb.IsAllowUpload(fileInfo.Extension))
                {
                    message = string.Format(GE.Global("WebFilesController_Unsupported"), "\"" + fileInfo.Extension + "\"");
                    return 415;
                }

                if (file.ContentLength > (webContext.RootWeb.MaximumFileSize * 1000000))
                {
                    message = GE.Global("WebFilesController_Forbidden");
                    return 403;
                }
            }
            return 200;
        }

        private void AttachToPost(Post post)
        {
            if (Request.Files.Count == 0)
                return;

            var path = Server.MapPath("~/shared/public/forums/attachments/" + post.ID.ToString());

            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            for (int i = 0; i < Request.Files.Count; i++)
            {
                var file = Request.Files[i];
                if ((file.ContentLength > 0) && !string.IsNullOrEmpty(file.FileName))
                {
                    string fileName = Path.GetFileName(file.FileName);

                    var attach = new Attachment()
                    {
                        FileName = fileName,
                        ContentType = file.ContentType,
                        Post = post
                    };
                    forumContext.Attachments.Create(attach);
                    file.SaveAs(path + "\\" + fileName);
                }
            }
        }

        [SecurityAction("Community", "Delete threads or posts", "Allows user can delete threads and posts.",
             TitleResName = "SA_DeleteThreadAndPosts",
             DescResName = "SA_DeleteThreadAndPostsDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Delete(int id)
        {
            Post post = forumContext.Posts.Find(id);
            var wrapper = new PostWrapper(post);
            if (!wrapper.IsOwner(HttpContext) && !wrapper.IsModerator(HttpContext))
                return new HttpUnauthorizedResult();
            return View(post);
        }

        [HttpPost]
        [SecurityAction("Community", "Delete threads or posts", "Allows user can delete threads and posts.",
             TitleResName = "SA_DeleteThreadAndPosts",
             DescResName = "SA_DeleteThreadAndPostsDesc",
             PermssionSetResName = "SA_Community"
         )]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(int id, string deleteReason)
        {
            Post post = forumContext.Posts.Find(id);

            bool isThread = post.IsThread;

            string path = Server.MapPath("~/shared/public/forums/attachments/" + post.ID.ToString());
            if (Directory.Exists(path))
                Directory.Delete(path, true);

            var threadID = post.Thread.ID;
            var forumID = post.Thread.Forum.ID;

            if (isThread)
            {
                if (Notifier.IsSubscribed(post.Thread.SubscriptionToken))
                    Notifier.Unsubscribe(post.Thread.SubscriptionToken);
            }

            forumContext.Posts.Delete(id, deleteReason, User.Identity.Name);
            if (!User.Identity.Name.Equals(post.UserName))
                new DeletePostEvent(post).Raise(HttpContext);

            forumContext.SaveChanges();

            if (isThread)
                return RedirectToAction("Threads", "Forum", new { id = forumID });
            else
                return RedirectToAction("Index", "Thread", new { id = threadID });
        }


        //[SecurityAction("Community", "Move post", "Allows user can move post among fourms.",
        //     TitleResName = "SA_MovePost",
        //     DescResName = "SA_MovePostDesc",
        //     PermssionSetResName = "SA_Community"
        // )]
        [Authorize]
        public ActionResult Move(int id)
        {
            var thread = forumContext.Threads.Find(id);
            var wrapper = new ThreadWrapper(thread);

            if (!wrapper.IsModerator(HttpContext) && !wrapper.IsModerator(HttpContext))
                return new HttpUnauthorizedResult();

            ViewBag.Provider = new ForumHierarchicalNodeProvider(forumContext.Forums);
            return View(thread);
        }

        //[SecurityAction("Community", "Move post", "Allows user can move post among fourms.",
        //     TitleResName = "SA_MovePost",
        //     DescResName = "SA_MovePostDesc",
        //     PermssionSetResName = "SA_Community"
        // )]
        [Authorize]
        [HttpPost, ValidateAntiForgeryToken]
        public ActionResult Move(int id, int forumID)
        {
            //Service.Move(id, forumID);
            var thread = forumContext.Threads.Find(id);
            if (thread.ForumID.Equals(forumID))
                return RedirectToAction("Move",id);

            forumContext.Threads.Move(id, forumID);
            forumContext.SaveChanges();

            return RedirectToAction("Index", "Thread", new { Area = AREA, id = id });
        }

        [PersonalOnly, Pagable]
        [MyControlPanel(ResBaseName = "forums", ResKey = "MyThreads", Group = "Community")]
        public ActionResult MyThreads(QueryParams query)
        {
            //int total = 0;
            //var model = forumContext.Threads.GetUserThreads(User.Identity.Name, query.Index - 1, query.Size, out total);
            int total = 0;
            var fields = !string.IsNullOrEmpty(query.OrderBy) ? query.OrderBy.Split('~') : null;
            var sortingField = (fields != null && fields.Count() > 0) ? fields[0] : "";
            var sortOrder = SortingOrders.Asc;

            if (fields != null && fields.Count() > 1)
            {
                if (fields[1].Equals("desc", System.StringComparison.OrdinalIgnoreCase))
                    sortOrder = SortingOrders.Desc;
            }

            var model = forumContext.Threads.GetUserThreads(User.Identity.Name, sortingField, sortOrder, query.Index - 1, query.Size, out total);

            return View(new ModelWrapper<Thread>()
            {
                Model = model,
                Total = total
            });
        }

        [SecurityAction("Community", "Edit post", "Allows user can edit post content",
             TitleResName = "SA_EditPost",
             DescResName = "SA_EditPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Edit(int id, bool notifyMe, FormCollection form)
        {
            var post = forumContext.Posts.Find(id);

            if (Request.Files.Count > 0)
            {
                var msg = "";
                var code = CheckUploadedFiles(out msg);

                if (code != 200)
                {
                    ModelState.AddModelError("", msg);
                    ViewBag.Forum = post.Thread.Forum;
                    return View(post);
                }
            }

            if (TryUpdateModel(post, "Post", new string[] { "Body", "Title" }))
            {
                if (ModelState.IsValid)
                {
                    if (post.IsThread)
                        post.Thread.Title = post.Title;

                    post.LastModified = DateTime.Now;
                    forumContext.Update(post.Thread);
                    forumContext.Update(post);
                    forumContext.SaveChanges();

                    #region Attachements
                    if (Request.Files.Count > 0)
                    {
                        AttachToPost(post);
                        forumContext.SaveChanges();
                    }

                    string keyName = "deletedAttachments";
                    if (form.AllKeys.Contains(keyName))
                    {
                        if (!string.IsNullOrEmpty(form[keyName]))
                        {
                            var keys = form[keyName].Split(',');
                            foreach (var key in keys)
                            {
                                var attachID = 0;
                                int.TryParse(key, out attachID);
                                if (attachID > 0)
                                {
                                    var attach = forumContext.Attachments.Find(attachID);
                                    forumContext.Attachments.Delete(attach);
                                    var filename = Server.MapPath("~/shared/public/forums/attachments/" + post.ID.ToString() + "/" + attach.FileName);
                                    if (System.IO.File.Exists(filename))
                                        System.IO.File.Delete(filename);
                                }
                            }
                            forumContext.SaveChanges();
                        }
                    }
                    #endregion

                    var token = post.SubscriptionToken;
                    var user = User.Identity.Name;
                    bool isSubscribed = Notifier.IsSubscribed(token, user);

                    if (notifyMe)
                    {
                        if (!isSubscribed)
                            Notifier.Subscribe(token, user);
                    }
                    else
                    {
                        if (isSubscribed)
                            Notifier.Unsubscribe(token, user);
                    }

                    return RedirectToAction("Index", "Thread", new { Area = AREA, id = post.Thread.ID });
                }
            }

            return View(post);
        }

        [SecurityAction("Community", "Edit post", "Allows user can edit post content",
             TitleResName = "SA_EditPost",
             DescResName = "SA_EditPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Edit(int id)
        {
            var post = forumContext.Posts.Find(id);
            if (post == null)
                return HttpNotFound();
            ViewBag.IsSubscribed = Notifier.IsSubscribed(post.SubscriptionToken, User.Identity.Name);

            return View(post);
        }

        [Authorize]
        [SecurityAction("Community", "Reply post", "Allows user can reply post.",
             TitleResName = "SA_ReplyPost",
             DescResName = "SA_ReplyPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Reply(int id, int parentID)
        {
            var post = forumContext.Posts.Find(parentID);
            if (post == null)
                return HttpNotFound();
            ViewBag.IsSubscribed = Notifier.IsSubscribed(post.SubscriptionToken, User.Identity.Name);
            return View(post);
        }

        [SecurityAction("Community", "Reply post", "Allows user can reply post.",
             TitleResName = "SA_ReplyPost",
             DescResName = "SA_ReplyPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Reply(int id, int parentID, bool notifyMe, Post post)
        {
            var parentPost = forumContext.Posts.Find(parentID);
            var forum = new ForumWrapper(parentPost.Thread.Forum, forumContext);

            var msg = "";
            var code = CheckUploadedFiles(out msg);

            if (code != 200)
            {
                ModelState.AddModelError("", msg);
                ViewBag.Editing = post;
                return View(parentPost);
            }

            if (ModelState.IsValid)
            {
                int _pos = parentPost.Thread.Posts.Max(p => p.Pos) + 1;
                var parentThread = parentPost.Thread;

                if (forum.IsModerated && forum.IsModerator(this.HttpContext))
                {
                    post.IsApproved = true;
                    post.Approved = DateTime.Now;
                    post.Moderator = User.Identity.Name;
                }
                else
                {
                    post.IsApproved = !forum.IsModerated;
                }

                post.Pos = _pos;
                post.UserName = User.Identity.Name;
                post.Thread = parentThread;
                post.LastModified = DateTime.Now;
                post.Posted = DateTime.Now;
                post.ParentID = parentID;
                post.ForumID = parentThread.ForumID;
                forumContext.Posts.Create(post);

                AttachToPost(post);

                forumContext.SaveChanges();

                if (notifyMe)
                    Notifier.Subscribe(post.Thread.SubscriptionToken, User.Identity.Name);

                if (post.IsApproved)
                {
                    parentThread.TotalPosts++;
                    parentThread.LastPostID = post.ID;
                    parentThread.Forum.LastPostID = post.ID;
                    parentThread.Forum.TotalPosts++;
                    forumContext.SaveChanges();

                    new NewPostEvent(post).Raise(HttpContext);
                }

                UpdateUserForumProfile();
                return RedirectToAction("Index", "Thread", new { Area = AREA, id = parentPost.Thread.ID });
            }
            ViewBag.Editing = post;
            return View(parentPost);
            //return RedirectToAction("Index", "Thread", new { Area = AREA, id = post.Thread.ID });
        }

        private void UpdateUserForumProfile(string userName = "")
        {
            var profile = HttpContext.Profile;

            if (!string.IsNullOrEmpty(userName))
            {
                profile = new System.Web.Profile.ProfileBase();
                profile.Initialize(userName, true);
            }
            else
            {
                if (Request.IsAuthenticated)
                    userName = User.Identity.Name;
            }

            if (profile != null)
            {
                //var points = (int)profile["Points"];
                var userTotalThreads = forumContext.Threads.Count(t => !t.IsDeleted && t.IsApproved && t.Author.Equals(userName));
                var userTotalPosts = forumContext.Posts.Count(t => !t.IsDeleted && !t.IsThread && t.IsApproved && t.UserName.Equals(userName));

                var rank = forumContext.Ranks.Find(r => r.MinimumPosts <= userTotalPosts && r.MinimumPosts > userTotalPosts);
                if (rank == null)
                    rank = forumContext.Ranks.All().OrderBy(r => r.MinimumPosts).FirstOrDefault();
                var group = profile.GetProfileGroup("Forum");
                if (rank != null)
                {
                    group["Rank"] = rank.Name;
                    group["Medal"] = rank.ImageUrl;
                }
                group["TotalThreads"] = userTotalThreads;
                group["TotalPosts"] = userTotalPosts;
                profile.Save();
            }
        }

        [SecurityAction("Community", "Audit post", "Allows user can audit the unappove posts",
             TitleResName = "SA_AuditPost",
             DescResName = "SA_AuditPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Audit(int id)
        {
            var post = forumContext.Posts.Find(id);

            if (post != null)
            {
                forumContext.Posts.Audit(id, User.Identity.Name);
                forumContext.SaveChanges();

                new NewPostEvent(post).Raise(HttpContext);

                UpdateUserForumProfile(post.UserName);
            }

            return RedirectToAction("Index", "Thread", new { Area = AREA, id = post.ThreadID  });
        }

        [SecurityAction("Community", "Pin post", "Allows user can pin post",
             TitleResName = "SA_PinPost",
             DescResName = "SA_PinPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Pin(int id)
        {
            forumContext.Threads.Pin(id, true);
            forumContext.SaveChanges();
            return RedirectToAction("Index", "Thread", new { Area = AREA, id = id });
        }

        [SecurityAction("Community", "Unpin post", "Allows user can unpin post",
             TitleResName = "SA_UnpinPost",
             DescResName = "SA_UnpinPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Unpin(int id)
        {
            forumContext.Threads.Pin(id, false);
            forumContext.SaveChanges();
            return RedirectToAction("Index", "Thread", new { Area = AREA, id = id });
        }

        [SecurityAction("Community", "Unlock post", "Allows user can unlock the post",
             TitleResName = "SA_UnlockPost",
             DescResName = "SA_UnlockPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Unlock(int id)
        {
            forumContext.Threads.Lock(id, false);
            forumContext.SaveChanges();
            return RedirectToAction("Index", "Thread", new { Area = AREA, id = id });
        }

        [SecurityAction("Community", "Lock post", "Allows user can lock the post",
             TitleResName = "SA_LockPost",
             DescResName = "SA_LockPostDesc",
             PermssionSetResName = "SA_Community"
         )]
        public ActionResult Lock(int id)
        {
            forumContext.Threads.Lock(id, true);
            forumContext.SaveChanges();
            return RedirectToAction("Index", "Thread", new { Area = AREA, id = id });
        }

    }
}
